package com.zer0.tga;

import java.awt.Canvas;
import java.awt.Color;
import java.awt.DisplayMode;
import java.awt.Font;
import java.awt.Graphics;
import java.awt.GraphicsDevice;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.awt.event.KeyEvent;
import java.awt.event.KeyListener;
import java.awt.image.BufferStrategy;
import java.util.LinkedList;
import java.util.Queue;

import javax.swing.JFrame;


//import com.Zer0.tga.Game;
import com.zer0.tga.art.Screen;
import com.zer0.tga.input.KeyInputHandler;
import com.zer0.tga.input.Keys;
import com.zer0.tga.level.Level;
import com.zer0.tga.level.Room;
import com.zer0.tga.math.Coordinate;
import com.zer0.tga.tile.Tile;
import com.zer0.tga.tile.WallTile;

public class Game extends Canvas implements Runnable, KeyListener {
	
	private static final int SCREEN_HEIGHT = Tile.HEIGHT*Room.ROOM_Y; //600;
	private static final int SCREEN_WIDTH = Tile.WIDTH*Room.ROOM_X; //SCREEN_HEIGHT*4/3;
	private static final int UI_HEIGHT = 40;//(SCREEN_WIDTH*Toolkit.getDefaultToolkit().getScreenSize().height/Toolkit.getDefaultToolkit().getScreenSize().width) - SCREEN_HEIGHT;
	private static final double SCALE = (double)(Toolkit.getDefaultToolkit().getScreenSize().height) / (SCREEN_HEIGHT + UI_HEIGHT);
	
	private boolean gameRunning = false;
	
	public Keys keys = new Keys();
	
	private String fpsInfo = " ";
	
	private Level level;
	/*
    private Screen screen = new Screen((int)(SCREEN_WIDTH*SCALE), (int)(SCREEN_HEIGHT*SCALE));
    private Screen uiscreen = new Screen((int)(SCREEN_WIDTH*SCALE),(int)(UI_HEIGHT*SCALE));
	*/
	private Screen screen = new Screen(SCREEN_WIDTH, SCREEN_HEIGHT);
    private Screen uiscreen = new Screen(SCREEN_WIDTH,UI_HEIGHT);
	
    public boolean shouldRender;
   
    public int direction = 2;
    
	/**
	 * 
	 */
	private static final long serialVersionUID = 7153991173738458627L;

	
	public Game() {

		this.addKeyListener(new KeyInputHandler(keys));
		//this.addMouseListener ~
		/*
		this.setPreferredSize(new Dimension((int)(SCREEN_WIDTH*SCALE),(int)((SCREEN_HEIGHT+UI_HEIGHT)*SCALE)));
		this.setMinimumSize(new Dimension((int)(SCREEN_WIDTH*SCALE),(int)((SCREEN_HEIGHT+UI_HEIGHT)*SCALE)));
	    this.setMaximumSize(new Dimension((int)(SCREEN_WIDTH*SCALE),(int)((SCREEN_HEIGHT+UI_HEIGHT)*SCALE)));
	    */
	  //  TitleMenu menu = new TitleMenu(GAME_WIDTH, GAME_HEIGHT);
      //  addMenu(menu);
        //addKeyListener(this);
		
		// request the focus so key events come to us
		requestFocus();

		// create the buffering strategy which will allow AWT
		// to manage our accelerated graphics
	//	createBufferStrategy(2);
	//	strategy = getBufferStrategy();
		
		// initialise the entities in our game so there's something
		// to see at startup
	//	initEntities();
	}
	
	public static void main(String argv[]) {
		
	/*	Level lvl = new Level();
		
		lvl.createLevel(36);
		
		lvl.testOutput();
		return;*/
		
		//Toolkit toolkit =  Toolkit.getDefaultToolkit ();
		//Dimension dim = toolkit.getScreenSize();
		  
		//SCALE = (double)dim.width / SCREEN_WIDTH;
		//SCALE = (double)(dim.height) / (SCREEN_HEIGHT + UI_HEIGHT);
		//SCALE = 0.5;
		Game g = new Game();
		
		// create a frame to contain our game
		JFrame frame = new JFrame("Teemo's Great Adventure");
		
		// add game to panel, add panel to frame
		//JPanel panel = new JPanel(new BorderLayout());
		//panel.add(g);
		
		frame.add(g);
		
		//frame.setContentPane(panel);
		  
		
		// make the window
		frame.setUndecorated(true);
		frame.pack();
		frame.setResizable(false);
	    frame.setLocationRelativeTo(null);
	    frame.setIgnoreRepaint(true);
	    frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // alttaki yerine bu var?
		frame.setVisible(true);
	
		
		GraphicsEnvironment ge = GraphicsEnvironment.getLocalGraphicsEnvironment();
		GraphicsDevice gd = ge.getDefaultScreenDevice();
		//GraphicsConfiguration gc = gd.getDefaultConfiguration();

		gd.setFullScreenWindow( frame );
		if( gd.isDisplayChangeSupported() ) {
			DisplayMode dm = new DisplayMode(Toolkit.getDefaultToolkit().getScreenSize().width ,Toolkit.getDefaultToolkit().getScreenSize().height, 32, 60 );
			gd.setDisplayMode(dm);
		}
	
		g.start();		
		
	}
	
	public void start() {
		gameRunning = true;
        Thread gameThread = new Thread(this);
        gameThread.setPriority(Thread.MAX_PRIORITY);
        gameThread.start();
    }
	
	
    public void stop() {
    	gameRunning = false;
       
    }
/*
    // BEEP BOOP RUN
    private static int MAX_FPS = 60;
    private static int MAX_FRAMESKIP = 5;
    private static int FRAME_PERIOD = 1000 / MAX_FPS;

    public void run() {


    	init();
    	//level.createTestLevel();
    	level.createLevel(20);

    	int fps=60;
    	//int tickTime=0;
    	long lastLoopTime=0;

    	long beginTime;     // the time when the cycle begun
    	long timeDiff;      // the time it took for the cycle to execute
    	int sleepTime;      // ms to sleep (<0 if we're behind)
    	int framesSkipped;  // number of frames being skipped 

    	while (gameRunning) {
    		beginTime = System.currentTimeMillis();
    		framesSkipped=0;

    		tick();

    		BufferStrategy bs = getBufferStrategy();
    		if (bs == null) {
    			createBufferStrategy(3);
    			continue;
    		}

    		shouldRender = true;

    		if (shouldRender) {
    			//   frames++;
    			Graphics g = bs.getDrawGraphics();

    			render(g);

    			if (shouldRender) {
    				if (bs != null) {
    					bs.show();
    				}

    			}
    		}


    		timeDiff = System.currentTimeMillis() - beginTime;

    		sleepTime = (int)(FRAME_PERIOD - timeDiff);
    		if(sleepTime > 0)
    			try {
    				Thread.sleep(sleepTime);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}

    		while (sleepTime < 0 && framesSkipped < MAX_FRAMESKIP) {
    			// we need to catch up
    			// update without rendering
    			tick();
    			// add frame period to check if in next frame
    			sleepTime += FRAME_PERIOD;
    			framesSkipped++;
    		}


    	}


    	
    			if(System.currentTimeMillis() - lastLoopTime >1000)
        		{
        			System.out.println("FPS: " + fps);
        			lastLoopTime = System.currentTimeMillis();
        			//tickTime = 20;//1000/fps;
        			fps=0;
        		}
        		fps++;
    	
    }

*/

    public boolean death=false;
    
    public void treeIt(Coordinate c)
    {
    	if(level.rooms[level.currentRoom_x][level.currentRoom_y].RoomInterior[c.x+(c.y*Room.ROOM_X)] instanceof WallTile)
    	{
    		death = true;
    		paused = true;
    	}
    	else // if FoodTile
    	{
    		level.rooms[level.currentRoom_x][level.currentRoom_y].RoomInterior[c.x+(c.y*Room.ROOM_X)] = new WallTile();
    		level.rooms[level.currentRoom_x][level.currentRoom_y].RoomInterior[c.x+(c.y*Room.ROOM_X)].init(level,c.x , c.y);
    		snake.add(c);
    	}
    	
    }
    
    public void plainIt(Coordinate c)
    {
    	level.rooms[level.currentRoom_x][level.currentRoom_y].RoomInterior[c.x+(c.y*Room.ROOM_X)] = new Tile();
        level.rooms[level.currentRoom_x][level.currentRoom_y].RoomInterior[c.x+(c.y*Room.ROOM_X)].init(level,c.x , c.y);
        
    }
    
    public int score = 0;

 //MOJAM RUN
    public void run() {
        long lastTime = System.nanoTime();
        double unprocessed = 0;
        int frames = 0;
        long lastTimer1 = System.currentTimeMillis();

        int tickk= 0, loop=0;
        
        init();
        
        //tailPos = new Coordinate(oldTile.c.x,oldTile.c.y);

        
//        if (!isMultiplayer) {
//            createLevel();
//        }

        int toTick = 0;

        long lastRenderTime = System.nanoTime();
        int min = 999999999;
        int max = 0;

        while (gameRunning) {
        	loop++;
            if (!this.hasFocus()) {
                keys.release();
            }
            
            double nsPerTick = 1000000000.0 / 60;
            boolean shouldRender = false;
            //System.out.print(unprocessed + "-");
            while (unprocessed >= 1) {
                toTick++;
                unprocessed -= 1;
            }

            int tickCount = toTick;
            //System.out.println(tickCount);
            if (toTick > 0 && toTick < 3) {
                tickCount = 1;
            }
            if (toTick > 20) {
                toTick = 20;
            }

            for (int i = 0; i < tickCount; i++) {
                toTick--;
                //long before = System.nanoTime();
                tick();
                tickk++;
                //long after = System.nanoTime();
                //System.out.println("Tick time took " + (after - before) * 100.0 / nsPerTick + "% of the max time");
                shouldRender = true;
            }
//            shouldRender = true;

            BufferStrategy bs = getBufferStrategy();
            if (bs == null) {
                createBufferStrategy(3);
                //createBufferStrateg
                continue;
            }
            if (shouldRender) {
                frames++;
                Graphics g = bs.getDrawGraphics();

                

                render(g);

                long renderTime = System.nanoTime();
                int timePassed = (int) (renderTime - lastRenderTime);
                if (timePassed < min) {
                    min = timePassed;
                }
                if (timePassed > max) {
                    max = timePassed;
                }
                lastRenderTime = renderTime;
            }

            long now = System.nanoTime();
            unprocessed += (now - lastTime) / nsPerTick;
            lastTime = now;

            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            if (shouldRender) {
                if (bs != null) {
                    bs.show();
                }
            }

            if (System.currentTimeMillis() - lastTimer1 > 1000) {
                lastTimer1 += 1000;
                fpsInfo = "FPS:" + frames + " Tick:" + tickk + " Loop:" + loop + " Pause:" +paused + " Score:"+ score; 
                System.out.println(fpsInfo);
                frames = 0;
                tickk = 0;
                loop = 0;
            }
        }
    }

   
    public void run4() {


    	init();
    	//level.createTestLevel();
    	level.createLevel(20);

    	int fps=0;
    	int tickCount=0;
    	long lastlooptime=0;
    	int frameCount = 0;
    	int maxFrame =60;
    	int loopTime = (1000/maxFrame); // 60 times in a sec
    	long tickTime;
    	long renderTime;
    	int remainingTime = 0;
    	int totalRenderTime = 0;
    	int totalTickTime = 0;
    	int totalSleepTime = 0;
    	
    	while (gameRunning) 
    	{
    		frameCount++;
    			
    		tickTime = System.currentTimeMillis();
    		tick();
    		tickCount++;
    		tickTime = System.currentTimeMillis() - tickTime;
    		
    		//System.out.print("TickTime: " + tickTime);
    		totalTickTime += tickTime;
    		remainingTime += (int) (loopTime-tickTime);
    		//graphic

    		if(remainingTime > 0)
    			shouldRender = true;
    		
    		if (shouldRender) 
    		{
    			
    			BufferStrategy bs = getBufferStrategy();
    			if (bs == null) {
    				createBufferStrategy(3);
    				continue;
    			}

    			Graphics g = bs.getDrawGraphics();


    			renderTime = System.currentTimeMillis();
    			render(g);
    			renderTime = System.currentTimeMillis() - renderTime;
    			totalRenderTime += renderTime;
    			//System.out.println("RenderTime: " + renderTime);
    			remainingTime -= renderTime;

    			
    			if(remainingTime > 0)
    			{
    				
    				fps++;
    				if (bs != null) 
    				{
    					bs.show();
    				}
    			
    				try {
    					if(remainingTime>5)
    					{
    						Thread.sleep(5);
    						totalSleepTime += 5;
    						remainingTime -=5;		
    					}
    					else
    					{
    						Thread.sleep(remainingTime);
    						totalSleepTime += remainingTime;
    						remainingTime = 0;
    					}
    				//Thread.sleep(1);
    				} catch (InterruptedException e) {
    					e.printStackTrace();
    				}
    				
    				
    			}
    			//else

    			shouldRender = false;
    		}

    


    	
    	if(System.currentTimeMillis() - lastlooptime >1000)
    	{
    		lastlooptime = System.currentTimeMillis();
    		System.out.println("FPS: " + fps + " Tick: " + tickCount + " Frame: " + frameCount);
    		System.out.println("R: " + totalRenderTime + " T: " + totalTickTime + " S: " + totalSleepTime + " Sum: " + (totalRenderTime+totalTickTime+totalSleepTime));
    		fps=0;
    		tickCount=0;
    		frameCount=0;
    		remainingTime=0;
    		totalRenderTime=0;
    		totalSleepTime=0;
    		totalTickTime=0;
    	}
    	
    	}

    }


   public Queue<Coordinate> snake = new LinkedList<Coordinate>();

   public boolean paused=true;


    public void run3() {


    	init();
    	//level.createTestLevel();
    	level.createLevel(20);
    	
    	int fps=0;
    	int tickCount=0;
    	long lastlooptime=0;
    	long lastRenderTime=0;
    	int renderTime = 1000/60; // 60 times in a sec
    	int sleepTime = 1;
    	while (gameRunning) {
    		
    		
    		tick();
    		tickCount++;
    		
    		
    		//graphic
   
    		BufferStrategy bs = getBufferStrategy();
    		if (bs == null) {
    			createBufferStrategy(3);
    			continue;
    		}

    	//	if(System.currentTimeMillis() - lastRenderTime > renderTime )
    		if(System.currentTimeMillis() - lastRenderTime > 0 || (tickCount>20))
    		{
    			shouldRender = true;
    			lastRenderTime = System.currentTimeMillis();
    			fps++;
    		}

    		if (shouldRender) 
    		{

    			Graphics g = bs.getDrawGraphics();

    			render(g);

    			if (shouldRender) {
    				if (bs != null) {
    					bs.show();
    				}

    			}
    			try {
    				Thread.sleep(sleepTime);
    				//Thread.sleep(1);
    			} catch (InterruptedException e) {
    				e.printStackTrace();
    			}
    			

        		shouldRender = false;
    		}
    		
    		if(System.currentTimeMillis() - lastlooptime >1000)
    		{
    			lastlooptime = System.currentTimeMillis();
    			System.out.println("FPS: " + fps + " Tick: " + tickCount + " SleepTime: " + sleepTime + " RenderTime: " +renderTime);
    			
    			if(fps>60 && fps<63)
    				sleepTime++;
    			else if(fps>55 && fps<58 && sleepTime >1)
    				sleepTime--;
    			//else if (fps<55 && sleepTime > 2)
    			//	sleepTime-=2;
    			else if(fps>63)
    			{
    				sleepTime+=2;
    			}
    			
    			if(tickCount>fps && renderTime>1)
    			{
    				renderTime--;
    			}
    		 	   			
    			fps=0;
    			tickCount=0;
    		}
    		
    	}


    }
    
    public int moveTime=0;
    
    public Coordinate headPos;
    public Coordinate tailPos;
    
    
    private void tick() {
		// TODO Auto-generated method stub
    	
    	
    	keys.tick();
    	/*
        if (keys.map_up.wasReleased()) {
        	if(level.hasNorthRoom())
        		level.goNorthRoom(screen);//level.currentRoom_y--;
        }
        if (keys.map_down.wasReleased()) {
        	if(level.hasSouthRoom())
        		level.goSouthRoom(screen);//level.currentRoom_y++;
        }
        if (keys.map_left.wasReleased()) {
        	if(level.hasWestRoom())
        		level.goWestRoom(screen);//level.currentRoom_x--;
        }
        if (keys.map_right.wasReleased()) {
        	if(level.hasEastRoom())
        		level.goEastRoom(screen);//level.currentRoom_x++;
        }
        */
    	
    	if(keys.up.wasPressed()) 
    	{
    		if(direction!=3)
    			direction =1;
    	}	
    	else if(keys.right.wasPressed())
    	{
    		if(direction!=4)
    			direction =2;
    	}
    	else if(keys.down.wasPressed()) 
    	{
    		if(direction!=1)
    			direction =3;
    	}
    	else if(keys.left.wasPressed()) 
    	{
    		if(direction!=2)
    			direction =4;
    	}
    	else if(keys.pause.wasPressed())
    		paused = !paused;
    	
    	if(!paused)
    	{
    		if(moveTime>0)
    			moveTime--;
    		else
    		{
    			updatePosition();
    			moveTime = 10;
	
    		}
    		
    		if(death)
    		{
    			
    			death=false;
    			snake.clear();
    			init();
    		}
    	}
    	level.tick();
    	
    	
		
	}

	private void updatePosition() {
		Coordinate newHead = null;
		
		if(direction==1)
		{
			if( headPos.y > 0)
				newHead = new Coordinate(headPos.x,headPos.y-1);
			else
				newHead = new Coordinate(headPos.x,Room.ROOM_Y-1);
		}
		else if(direction==2)
		{
			if( headPos.x < Room.ROOM_X-1)
				newHead = new Coordinate(headPos.x+1,headPos.y);  	
			else
				newHead = new Coordinate(0,headPos.y);
		}
		else if(direction==3)
		{
			if( headPos.y < Room.ROOM_Y-1)
				newHead = new Coordinate(headPos.x,headPos.y+1);
			else
				newHead = new Coordinate(headPos.x,0);
		}
		else if(direction==4)
		{
			if( headPos.x > 0)
				newHead = new Coordinate(headPos.x-1,headPos.y);  	
			else
				newHead = new Coordinate(Room.ROOM_X-1,headPos.y);
		}
		
		
		treeIt(newHead);
		headPos = newHead;	
		plainIt(snake.poll());
		
		
	}

	private void render(Graphics g) {
    	// TODO Auto-generated method stub

    	level.render(screen);
    	level.renderUi(uiscreen);

    	g.setColor(Color.BLACK);
    	g.fillRect(0, 0, getWidth(), getHeight());
    	//System.out.println(getWidth() + " " +getHeight());
    	
    	g.translate((getWidth() - (int)(SCREEN_WIDTH*SCALE))/2, (getHeight() - (int)((SCREEN_HEIGHT+UI_HEIGHT)*SCALE))/2);
    	//g.translate(0, 0);
    	g.clipRect(0, 0, (int)(SCREEN_WIDTH*SCALE), (int)((SCREEN_HEIGHT+UI_HEIGHT)*SCALE));

    	if (level != null) {
    		g.drawImage(uiscreen.getImage(), 0, 0, (int)(SCREEN_WIDTH*SCALE), (int)(UI_HEIGHT*SCALE), null);	
    		g.drawImage(screen.getImage(), 0, (int)(UI_HEIGHT*SCALE), (int)(SCREEN_WIDTH*SCALE), (int)(SCREEN_HEIGHT*SCALE), null);
    		
    		g.setFont(g.getFont().deriveFont(Font.BOLD, 20));
    		g.drawString(fpsInfo, 25, 25);
    	}

    }


	
	private void init() {
		// TODO Auto-generated method stub
		level = new Level();
		
		level.createLevel(1);
        
        Tile oldTile = level.rooms[level.currentRoom_x][level.currentRoom_y].RoomInterior[(Room.ROOM_X*(Room.ROOM_Y-1))+1];
        treeIt(new Coordinate(oldTile.c.x,oldTile.c.y));
        treeIt(new Coordinate(oldTile.c.x+1,oldTile.c.y));
        treeIt(new Coordinate(oldTile.c.x+2,oldTile.c.y));
        treeIt(new Coordinate(oldTile.c.x+3,oldTile.c.y));
        treeIt(new Coordinate(oldTile.c.x+4,oldTile.c.y));
        
        headPos = new Coordinate(oldTile.c.x+4,oldTile.c.y);
		
        direction = 2;
	}

	@Override
	public void keyPressed(KeyEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void keyReleased(KeyEvent arg0) {
		// TODO Auto-generated method stub
		
	}

	@Override
	public void keyTyped(KeyEvent arg0) {
		// TODO Auto-generated method stub
		
	}

}
